package com.yuji.aacar.core.mvvm.apptask;

import android.os.Message;

import com.yuji.aacar.core.cores.event.CoreEventCenter;
import com.yuji.aacar.core.cores.services.CoreServiceMediator;
import com.yuji.aacar.core.cores.services.CoreServiceResponse;
import com.yuji.aacar.core.cores.services.Field_Method_Parameter_Annotation;
import com.yuji.aacar.core.cores.utils.CoreFileUtil;
import com.yuji.aacar.core.cores.utils.LogUtil;
import com.yuji.aacar.core.mvvm.appbase.CoreBaseViewModel;
import com.yuji.aacar.net.bean.BaseResult;
import com.yuji.aacar.net.bean.HttpCoreBaseRequest;
import com.yuji.aacar.net.core.HttpManager;

import org.json.JSONException;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ConnectException;
import java.net.ProtocolException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLException;

import rx.Observable;
import rx.Subscriber;
import rx.Subscription;
import rx.functions.Func1;


/**
 * <li>Package:com.ttp.core.mvvm.apptask</li>
 * <li>Author: lining</li>
 * <li>Date: 2017/2/15</li>
 * <li>Description:
 * /**
 * <br>
 * 网络请求的基本类，所有网络请求都统一调用该类中的 doTask(final ViewModel viewModel, final String
 * method)方法。</br>
 * <p>
 * <br>
 * 请求方法通过传入的viewmodel映射获取参数， 所以调用doTask之前确保viewmodel中请求相应的参数赋值正确。</br>
 * <p>
 * <br>
 * method是需要进行请求的方法名,方法名在ServiceMediator中会有定义 </br>
 * <p>
 * <br>
 * doTask的返回值是唯一的，用方法名+自增长的编号 </br>
 * <p>
 * <br>
 * removeTask(String token) 方法手动取消某个请求的时候，务必传入doTask返回的token，否则无效 </br>
 * <p>
 * <br>
 * 请求成功会调用refreshData(String method)方法。返回值会回填到activity的对应返回值属性中</br>
 * <p>
 * <br>
 * 请求失败会调用 requestFailedHandle(String method, int errorCode,String errorMsg)方法。
 * BaseActivity中会做统一错误处理，如果需要自行处理，请Override该方法</br>
 * </li>
 */

public class TaskManager {

    public static TaskManager taskManager;

    /**
     * 正在进行中的Task的Token集合 requestCode + TaskToken
     */
    private ConcurrentHashMap<String, TaskToken> requestCodeAndTokenMap = new ConcurrentHashMap<String, TaskToken>();

    /**
     * 所有的Tasktoken
     */
    private List<TaskToken> tokenList;


    public static TaskManager getInstance() {
        synchronized (TaskManager.class) {
            if (taskManager == null) {
                taskManager = new TaskManager();
            }
            return taskManager;
        }
    }


    /**
     * 线程开启任务 供UI调用
     *
     * @param viewModel  数据封装的viewModel对象
     * @param methodName 请求调用的方法名
     *                   例如: getHomePageINfo
     */
    public synchronized <T> TaskToken doTask(final CoreBaseViewModel viewModel, final String methodName, final HashMap<String, Object> params) {
        TaskToken taskToken = null;
        Observable observable = null;
        try {
            HttpCoreBaseRequest baseRequest = getHttpCoreBaseRequest(params, methodName);
            if (null == baseRequest){
                throw new IllegalArgumentException(
                        "doTask() your baseRequest should not be null, check your params!");
            }
            LogUtil.e("baseRequest", baseRequest);
            viewModel.setBaseRequest(baseRequest);
            viewModel.setRequestCode(baseRequest.getRequestCode());
            Method requestMethod = CoreServiceMediator.getApiMethodByName(methodName);
            if (null == requestMethod) {
                throw new IllegalArgumentException(
                        "doTask() your request method should not be null, check your apis !");
            }
            Object service = CoreServiceMediator.getHttpServiceApiByMethod(methodName);
            observable = (Observable) requestMethod.invoke(service, baseRequest);
            observable = HttpManager.getInstance().doHttpDeal(observable);
            LogUtil.e("observable", observable);
            final Subscriber mSubscriber = new Subscriber<CoreServiceResponse>() {
                private int mSubscriberRequestCode = baseRequest.getRequestCode();
                private String mSubscriberRequestMethod = methodName;
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                    if (null != e.getMessage()) {
                        e.printStackTrace();
                        LogUtil.e("-Throwable-type-", e);
                        LogUtil.e("-Throwable-type-", mSubscriberRequestCode);
                        LogUtil.e("-Throwable-onError-", e.getMessage());
                        if (e instanceof UnknownHostException || e instanceof ProtocolException ||e instanceof ConnectException){
                            LogUtil.e("-Throwable-UnknownHostException-", e.getMessage());
                            if (isContainTaskTokenByRequest(mSubscriberRequestMethod)){
                                CoreEventCenter.postMessage(CoreEventCenter.NETWOR_NOT_CONNECT, mSubscriberRequestCode, "请打开您的网络！", mSubscriberRequestMethod);
                            }
                            if (LogUtil.getLogType()){
                                CoreFileUtil.writeFileToPath(CoreFileUtil.LOG_FILE_FOR_SERVER_REPORT, e.getMessage());
                            }
                        } else if (e instanceof JSONException){
                            LogUtil.e("-Throwable-JSONException-", e.getMessage());
                            if (isContainTaskTokenByRequest(mSubscriberRequestMethod)){
                                CoreEventCenter.postMessage(CoreEventCenter.NETWOR_NOT_CONNECT,mSubscriberRequestCode, "服务器异常，请稍后再试！", mSubscriberRequestMethod);
                            }
                            if (LogUtil.getLogType()){
                                CoreFileUtil.writeFileToPath(CoreFileUtil.LOG_FILE_FOR_SERVER_REPORT, e.getMessage());
                            }
                        } else if(e instanceof SSLException){
                            LogUtil.e("-Throwable-SSLException-", e.getMessage());
                            if (isContainTaskTokenByRequest(mSubscriberRequestMethod)){
                                CoreEventCenter.postMessage(CoreEventCenter.NETWOR_NOT_CONNECT, mSubscriberRequestCode,"连接异常或者服务器异常，请稍后再试！", mSubscriberRequestMethod);
                            }
                            if (LogUtil.getLogType()){
                                CoreFileUtil.writeFileToPath(CoreFileUtil.LOG_FILE_FOR_SERVER_REPORT, e.getMessage());
                            }
                        } else {
                            //                            taskFailedHandle(response.getTokenObj());
                            LogUtil.e("-Throwable-业务异常-", e.getMessage());
                            if (isContainTaskTokenByRequest(mSubscriberRequestMethod)){
                                CoreEventCenter.postMessage(CoreEventCenter.BUSSINESS_SERVICE_ERROR, mSubscriberRequestCode, e.getMessage(), mSubscriberRequestMethod);
                            }
                            if (LogUtil.getLogType()){
                                CoreFileUtil.writeFileToPath(CoreFileUtil.LOG_FILE_FOR_CRASH_REPORT, e.getMessage());
                            }
                        }
                    }
                    cancelAllTask();
                }

                @Override
                public void onNext(CoreServiceResponse response) {
                    LogUtil.e("onNext--before--token size--", requestCodeAndTokenMap.size());
                    clearRequestCodeAndTaskTokenByRequestCode(String.valueOf(response.getRequestCode()));
                    LogUtil.e("onNext--after---token size--", requestCodeAndTokenMap.size());
                    LogUtil.e("onNext--after---token left--", requestCodeAndTokenMap);
                    ViewModelManager.getInstance().notifyViewModel(response);
                    //                            taskFailedHandle(response.getTokenObj());
                }
            };
            LogUtil.e("mSubscriber", mSubscriber);
            taskToken = getTaskToken(methodName, viewModel, mSubscriber);
            LogUtil.e("taskToken", taskToken);
            String requestCode = String.valueOf(viewModel.getRequestCode());
            LogUtil.e("requestCode", requestCode);
            cancelUnDoTaskBySameRequest(requestCode);
            viewModel.setTaskToken(taskToken);
            requestCodeAndTokenMap.put(requestCode, taskToken);
            observable.delaySubscription(0, TimeUnit.SECONDS)
                    .map(new Func1<BaseResult, CoreServiceResponse>() {
                        @Override
                        public CoreServiceResponse call(BaseResult baseResultMessage) {

                            LogUtil.e("core:<<call>>", "call");
                            CoreServiceResponse response = new CoreServiceResponse();
                            String requestCode = String.valueOf(baseResultMessage.getServiceCode());
                            response.setResultCode(baseResultMessage.getErrorCode());
                            response.setResponse(baseResultMessage.getResult());
                            response.setMessage(baseResultMessage.getErrorMessage());
                            TaskToken token = getTaskTokenByRequestCode(requestCode);
                            if (null != token) {
                                response.setTokenObj(token);
                                response.setRequestCode(token.requestCode);
                            }
                            LogUtil.e("core:<<response>>", response.toString());
                            return response;
                        }
                    }).flatMap(new Func1<CoreServiceResponse, Observable>() {

                @Override
                public Observable call(CoreServiceResponse response) {

                    //                    test200(response);
                    //                    Observable.error(viewModel.exception);
                    return Observable.just(response);
                }
            }).subscribe(mSubscriber);

        } catch (IllegalArgumentException e) {
            LogUtil.e("IllegalArgumentException",e);
//            taskFailedHandle(taskToken);
            removeTask(taskToken);
        } catch (IllegalAccessException e) {
            LogUtil.e("IllegalAccessException",e);
//            taskFailedHandle(taskToken);
            removeTask(taskToken);
        } catch (InvocationTargetException e) {
            LogUtil.e("InvocationTargetException",e);
//            taskFailedHandle(taskToken);
            removeTask(taskToken);
        } catch (Exception e) {
            LogUtil.e("Exception",e);
//            taskFailedHandle(taskToken);
            removeTask(taskToken);
            LogUtil.e("doTask", "请求发生异常!");

        }
        return taskToken;
    }


    private HttpCoreBaseRequest getHttpCoreBaseRequest(HashMap<String, Object> params, String methodName) throws Exception {
        HashMap<CoreServiceMediator, Method> serviceMediator = CoreServiceMediator.getServiceMediatorAndMethodByMethod(methodName);
        HttpCoreBaseRequest request = null;
        if (null == serviceMediator){
            return null;
        }
        if (!serviceMediator.isEmpty()) {
            if (serviceMediator.size() == 1) {
                for (CoreServiceMediator key : serviceMediator.keySet()) {
                    Method method = serviceMediator.get(key);
                    LogUtil.e("getHttpCoreBaseRequest===key== ", key);
                    LogUtil.e("getHttpCoreBaseRequest===value== ", serviceMediator.get(key));
                    Object[] objects = null;
                    if (null != params){
                        // 获取参数注
                        if (method
                                .isAnnotationPresent(Field_Method_Parameter_Annotation.class)) {
                            Field_Method_Parameter_Annotation annotation = method
                                    .getAnnotation(Field_Method_Parameter_Annotation.class);
                            if (annotation.args() != null) {
                                objects = new Object[annotation.args().length];
                                //根据objects得到  map参数
                                for (int i = 0; i < annotation.args().length; i++) {
                                    String strValue = annotation.args()[i];
                                    objects[i] = params.get(strValue);
                                }
                            } else {
                                objects = new Object[0];
                            }
                        } else {
                            objects = new Object[0];
                        }
                    } else {
                        objects = new Object[0];
                    }

                    LogUtil.e("requestParams", Arrays.toString(objects));
                    request = (HttpCoreBaseRequest) method.invoke(
                            serviceMediator, objects);
                }
            } else {
                return request;
            }
        }
        return request;

    }

    private void test200(CoreServiceResponse response) {
        if (response.getResultCode() != 200) {
            String requestCode = String.valueOf(response.getRequestCode());
            TaskToken token = getTaskTokenByRequestCode(requestCode);
            LogUtil.e("--Throwable---token--", token);
            clearRequestCodeAndTaskTokenByRequestCode(requestCode);
            //                                response.setResultCode(1000);
            LogUtil.e("--Throwable---CoreServiceResponse--", response);
            LogUtil.e("--Throwable---错误业务码--", response.getRequestCode());
            ViewModelManager.getInstance().notifyViewModel(response);
        }
    }

    /**
     * 同一个任务，如果没有执行的任务，就先取消掉
     */
    private synchronized void cancelUnDoTaskBySameRequest(String requestCode) {
        TaskToken token = getTaskTokenByRequestCode(requestCode);
        if (null != token) {
            LogUtil.e("---cancelUnDoTaskBySameRequest---", token);
            cancelTask(token);
        }
    }

    /**
     * 取消请求返回
     *
     * @param <T>
     */
    @SuppressWarnings({"unchecked"})
    private <T> void cancelTaskHandle(TaskToken token) {
        //        CoreServiceResponse<T> response = new CoreServiceResponse<T>();
        //        response.setTokenObj((T) token);
        //        response.setReturnCode(CoreServiceMediator.Service_Return_CancelRequest);
        //        response.setReturnDesc("取消请求");
        //
        //        Message message = new Message();
        //        message.what = response.getReturnCode();
        //        message.obj = response;
        //
        //        ViewModelManager.manager().getHandler().sendMessage(message);
    }

    /**
     * 请求失败返回
     */
    private <T> void taskFailedHandle(TaskToken token) {
        CoreServiceResponse<T> response = new CoreServiceResponse<T>();
        response.setTokenObj(token);
        response.setResultCode(CoreServiceMediator.Service_Return_Error);
        response.setMessage("请求失败");
        Message message = new Message();
        message.what = response.getResultCode();
        message.obj = response;
        ViewModelManager.getInstance().getHandler().sendMessage(message);
    }

    /**
     * 当前业务对象是否存活
     *
     * @param requestMethod 请求方法名
     */

    public Boolean isContainTaskTokenByRequest(String requestMethod) {

        Iterator<Map.Entry<String, TaskToken>> it = requestCodeAndTokenMap.entrySet().iterator();
        while (it.hasNext()){
            Map.Entry<String, TaskToken> keyValue = it.next();
            TaskToken token = keyValue.getValue();
            if (token.methodName.contentEquals(requestMethod)){
                LogUtil.e("-Throwable-业务异常--isContainTaskTokenByRequest--",token);
                return true;
            }
        }
        LogUtil.e("-Throwable-业务异常--isContainTaskTokenByRequest--",requestMethod+"不包含在业务对象中");
        return false;
    }

    public Boolean isContains(TaskToken token) {
        if (null == token) {
            return false;
        }
        return requestCodeAndTokenMap.get(String.valueOf(token.requestCode)) == null ? false : true;
    }

    /**
     * 取消Task任务 如果需要则需记录调用Task时返回的Token 根据此Token取消Task
     *
     * @param token 开启任务时返回的Token
     */
    private void removeTask(TaskToken token) {
        // 监视程序取消监视该Task
        //        TaskMonitor.getInstance().removeTask(token);
        if (token != null) {
            clearRequestCodeAndTaskTokenByRequestCode(String.valueOf(token.requestCode));
            LogUtil.e("---removeTask---token取消后---", requestCodeAndTokenMap);
            LogUtil.e("---要取消线程的对象---isUnsubscribed---", token);
            LogUtil.e("---isUnsubscribed---", token.socketToken.isUnsubscribed());
            if (!token.socketToken.isUnsubscribed()) {
                token.socketToken.unsubscribe();
            }
            LogUtil.e("---取消线程后---isUnsubscribed---", token.socketToken.isUnsubscribed());
            LogUtil.e("tokenRemove", token);
            LogUtil.e("tokenRemove left", requestCodeAndTokenMap.size());
            LogUtil.e("tokenRemove left", requestCodeAndTokenMap);
        }

    }

    /**
     * 对外的取消task任务方法
     */
    public synchronized void cancelTask(TaskToken token) {

        if (isContains(token)) {
            removeTask(token);
        }
    }

    /**
     * 取消所有的请求接口
     */
    public synchronized void cancelAllTask() {
        getAllCurrentTask();
        for (TaskToken token : tokenList) {
            removeTask(token);
        }
    }

    /**
     * 获取所有的token集合
     */
    public synchronized List<TaskToken> getAllCurrentTask() {
        Iterator iter = requestCodeAndTokenMap.entrySet().iterator();
        tokenList = Collections.synchronizedList(new ArrayList<TaskToken>());
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();
            TaskToken token = (TaskToken) entry.getValue();
            tokenList.add(token);
        }
        LogUtil.e("--getAllCurrentTask--",tokenList);
        return tokenList;
    }


    /**
     * Token生成器 token累加器
     */
    private synchronized <T> TaskToken getTaskToken(String method, CoreBaseViewModel vModel, Subscription subscription) {
        if (null == vModel) {
            return null;
        }
        LogUtil.e("----getTaskToken----", vModel.toString());
        TaskToken token = new TaskToken();
        token.viewModelId = vModel.getViewModelId();
        token.methodName = method;
        token.socketToken = subscription;
        token.requestCode = vModel.getRequestCode();
        LogUtil.e("----getTaskToken----", token.toString());
        return token;
    }


    private synchronized <T> TaskToken getTaskTokenByRequestCode(String requestCode) {
        LogUtil.e("---getTaskTokenByRequestCode---是否包含了这个请求code--", requestCodeAndTokenMap.containsKey(requestCode));
        if (requestCodeAndTokenMap.containsKey(requestCode)) {
            return requestCodeAndTokenMap.get(requestCode);
        }
        return null;
    }

    private synchronized void clearRequestCodeAndTaskTokenByRequestCode(String requestCode) {
        LogUtil.e("---getTaskTokenByRequestCode---requestCode--", requestCode);
        TaskToken token = getTaskTokenByRequestCode(requestCode);
        if (null != token && null != token.viewModelId) {
            requestCodeAndTokenMap.remove(String.valueOf(token.requestCode));
        }
    }


    // 给activity编号，保证唯一性
    private static long activityCounter = 0;

    public long getActivityCounter() {
        activityCounter += 1;
        return activityCounter;
    }

    public void resetActivityCounter() {
        activityCounter = 0;
    }

    /**
     * 请求超时处理后发送消息处理超时
     *
     * @param token TaskToken
     */
    private <T> void taskTimeoutHandle(TaskToken token) {
        if (null == token) {

        } else {
            CoreServiceResponse<T> response = new CoreServiceResponse<T>();
            response.setTokenObj(token);
            response.setResultCode(CoreServiceMediator.Service_Return_RequestTimeOut);
            response.setMessage(CoreServiceMediator.Service_Return_RequestTimeOut_Desc);

            Message message = new Message();
            message.what = response.getResultCode();
            message.obj = response;

            ViewModelManager.getInstance().getHandler().sendMessage(message);
        }

    }

    /**
     * 停止Task
     *
     * @param token TaskToken
     */
    public synchronized void stopTask(TaskToken token) {
        if (isContains(token)) {
            //            taskTimeoutHandle(token);
            removeTask(token);
        }
    }
}
